Hello friends! The question actually duplicates this post: https://stackoverflow.com/questions/64512330/avplayer-with-resourceloader-delegate-get-data-but-not-playing-video
My task is to play a video from a repository that requires a certificate. If I use a simple AVPlayer setup without setting a AVAssetResourceLoaderDelegate:
controller.player = AVPlayer(url: urlFromUnprotectedPlace)
the player perfectly opens urls from the unprotected storage and plays the video. But on trying to play video from a protected folder, it gives an error: "The certificate for this server is invalid". This problem is solved by setting a resourceLoader delegate:
let asset = AVURLAsset(url: customSchemeUrl)
asset.resourceLoader.setDelegate(myDelegate, queue: .main)
let item = AVPlayerItem(asset: asset)
controller.player = AVPlayer(playerItem: item)
And the delegate method itself:
func resourceLoader(
				_ resourceLoader: AVAssetResourceLoader,
				shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest
		) -> Bool {
				guard let url = url else {
						logger.log("nil url")
						return false
				}
				var request = URLRequest(url: url)
				request.httpMethod = "GET"
				request.setValue("bytes=0-", forHTTPHeaderField: "Range")
				
				let sessionTask = urlSession?.dataTask(with: request) { [weak self] data, response, error in
						if let error = error {
								self?.logger.log(error.localizedDescription)
								return
						}
						loadingRequest.contentInformationRequest?.contentType = AVFileType.mp4.rawValue
						loadingRequest.contentInformationRequest?.isByteRangeAccessSupported = true
						loadingRequest.response = response
						if let data = data {
								loadingRequest.contentInformationRequest?.contentLength = Int64(data.count)
								loadingRequest.dataRequest?.respond(with: data)
						}
						loadingRequest.finishLoading()
				}
				sessionTask?.resume()
				return true
		}
The data and the response come, the error is nil. The data is of the correct size. The response contains the status 206. The player's item has an observer of its state, but no one displays any errors - the player is just black and the download wheel is spinning.
But I went further. There is such a project: https://github.com/2ZGroupSolutionsArticles/Article_EZ002
I replaced my delegate with his delegate - identical behavior. But it also has the saving of the downloaded data to a local file. So, if after the completion of saving try to play this file - everything is OK, it is played.
My SwiftUI player:
final class MyVideoPlayer: UIViewControllerRepresentable {
		private let controller = AVPlayerViewController()
		private var playerItemObserver: MyPlayerItemStateObserver?
		private var resourceLoaderDelegate: EugeneResourceLoaderDelegate?
		
		private var playingStarted = false
		
		init(url: URL?) {
				guard let url = url else { return }
				resourceLoaderDelegate = EugeneResourceLoaderDelegate(withURL: url)
				resourceLoaderDelegate?.completion = downloadCompletion()
				controller.player = createPlayer()
		}
		
		func makeUIViewController(context: Context) -> AVPlayerViewController {
				controller
		}
		func updateUIViewController(_ uiViewController: AVPlayerViewController, context: Context) {}
		
		func play() -> some View {
				playingStarted = true
				controller.player?.play()
				return self
		}
}
extension MyVideoPlayer {
		private func createPlayer() -> AVPlayer? {
				guard
						let delegate = resourceLoaderDelegate,
						let streamingUrl = delegate.streamingAssetURL
				else {
						return nil
				}
				let asset = AVURLAsset(url: streamingUrl)
				asset.resourceLoader.setDelegate(delegate, queue: .main)
				playerItemObserver = MyPlayerItemStateObserver(asset: asset, logger: logger)
				if let item = playerItemObserver?.item {
						return AVPlayer(playerItem: item)
				}
				return nil
		}
		
		private func downloadCompletion() -> (URL?) -> Void {
				{ [weak self] localFileURL in
						guard let self = self else { return }
						guard let localFileURL = localFileURL else {
								print("Failed to download media file.")
								return
						}
						print("Media file saved to: \(localFileURL)")
						let status = self.controller.player?.timeControlStatus
						if self.playingStarted && status?.rawValue == 1 {
								self.controller.player = AVPlayer(url: localFileURL)
								self.controller.player?.play()
						}
				}
		}
}
I think I'm already out of ideas. I would be grateful for any help.
Upd. I printed out the state of the player and its currentItem before re-creating it to play the local file, and I got this:
player?.reasonForWaitingToPlay - AVPlayerWaitingWhileEvaluatingBufferingRateReason
player?.currentItem?.isPlaybackBufferEmpty - true
player?.currentItem?.duration - value: 0
Post
Replies
Boosts
Views
Activity
I generated tons of developer tokens with these generators:
https://github.com/pelauimagineering/apple-music-token-generator
https://github.com/ethanhuang13/CupertinoJWT
And I've recreated keys and identifiers many times in the apple developer account too.
But requests in my app to https://api.music.apple.com/v1/me/library/artists?offset= always returns 401.
Also this test returns 401:
curl -v -H 'Authorization: Bearer [developer token]' "https://api.music.apple.com/v1/test"
The most interesting thing is that the method
SKCloudServiceController().requestUserToken(forDeveloperToken: ...)
returns the user token without errors. Using the developer token I created.
I don't know what I'm doing wrong. I guess maybe the problem with my developer account with AM subscription and can't use it to test AM functionality? Or maybe AM service isn't working. But https://www.apple.com/support/systemstatus/ looks ok.
I'll be glad for any information.
Hello!
New families (.accessoryInline, .accessoryCircular, .accessoryRectangular) can work on both lock and home screens. But I need a strict separation of widgets for each screen. Have no idea how to construct condition.
Hello!
I receive events in this way:
let store = EKEventStore()
let predicate = store.predicateForEvents(withStart: startDate, end: endDate, calendars: nil)
let events = store.events(matching: predicate)
And remove one of them in this way:
try store.remove(event, span: .thisEvent, commit: true)
It removes event from iPhone's calendar, but does not from MacBook's. The event came from Google calendar. If I do these things with an event came from iCloud calendar, it works both on iPhone and MacBook. I have tried different ways to solve this problem but without success. I would appreciate any help.